package com.ericsson.mhealth.server;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import com.ericsson.mhealth.common.dto.FilterID;
import com.ericsson.mhealth.common.dto.ReadingID;
import com.ericsson.mhealth.gvf.GVFCommunicationException;
import com.ericsson.mhealth.gvf.GVFException;
import com.ericsson.mhealth.gvf.GVFManager;
import com.ericsson.mhealth.gvf.GVFReadingException;
import com.ericsson.mhealth.gvf.plugin.corbelt.CorBELTECGChannel;
import com.ericsson.mhealth.gvf.plugin.corbelt.CorBELTECGData;
import com.ericsson.mhealth.server.config.annotations.InjectLogger;
import com.ericsson.mhealth.server.model.MeasurementAtomicUnit;
import com.google.gson.Gson;
import com.google.inject.Inject;
import com.google.inject.Singleton;


@SuppressWarnings("serial")
@Singleton
public class MeasurementData extends HttpServlet {
	
	private static String DATA_AMOUNT = "dataAmount";
	private static String READING_ID = "readingId";
	
	@Inject private GVFManager gvfManager;
	@Inject private Gson gson;
	@InjectLogger Logger logger;
	
	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException  {
		ReadingID readingID = null;
		Integer dataAmount = null;
		CorBELTECGChannel corBELTECGChannel = null;
		PrintWriter out = resp.getWriter();
		
		Hashtable<String, String> requestParameters = extractRequestParameters(req);
		if(requestParameters.containsKey(READING_ID))
			readingID = new ReadingID(requestParameters.get(READING_ID));
		if(requestParameters.containsKey(DATA_AMOUNT))
			dataAmount = Integer.parseInt(requestParameters.get(DATA_AMOUNT));
		
		try {
			logger.info("Creating reading channel. Time: " + (new Date().getTime()));
			corBELTECGChannel = CorBELTECGChannel.createReadingChannel(gvfManager, readingID, new FilterID("CorBELTECGFilter"));
		} catch (GVFException e) {
			logger.error("An error occured while creating reading channel for reading (" + readingID.getIdentifier() + ")");
		}
		out.println(gson.toJson(generateData(fetchData(corBELTECGChannel, dataAmount))));
		
		out.flush();
	}
	
	/**
	 * This method is used for parsing http request string and extracting information from it.
	 * @param request The request from which to extract info.
	 * @return Returns Map<String, Object> with request parameters as Strings(Keys) and their
	 * 			corresponding values as Objects.
	 */
	private Hashtable<String, String> extractRequestParameters(HttpServletRequest request) {
		String paramValue = new String();
		Hashtable<String, String> parameters = new Hashtable<String, String>();
		
		paramValue = request.getParameter(DATA_AMOUNT);
		if (paramValue != null)
			parameters.put(DATA_AMOUNT, paramValue);
		
		paramValue = request.getParameter(READING_ID);
		if (paramValue != null)
			parameters.put(READING_ID, paramValue);
		
		return parameters;
	}
	
	/**
	 * Method gets number of samples from a channel.
	 * @param corbeltChannel channel to read samples from.
	 * @param dataAmount Number of samples to return. If this parameter is set to null
	 * 			method returns all available samples.
	 * @return
	 */
	private List<CorBELTECGData> fetchData(CorBELTECGChannel corbeltChannel, Integer dataAmount) {
		List<CorBELTECGData> data = new ArrayList<CorBELTECGData>();
		int counter = 0;
		
		try {
			logger.info("Requesting all data for reading. Time: " + (new Date().getTime()));
			corbeltChannel.requestAllData();
			logger.info("All data received. Time: " + (new Date().getTime()));
			corbeltChannel.waitForEndOfReading();
		
			logger.info("Starting to process data. Time: " + (new Date().getTime()));
			dataAmount = dataAmount!=null ? dataAmount : corbeltChannel.available();
			while(corbeltChannel.available()>0 && counter < dataAmount) {
				data.add(corbeltChannel.readChannelData());
				counter++;
			}
			logger.info("All data processed. Time: " + (new Date().getTime()));
		}
		catch (GVFReadingException e) {
			logger.error(e.getMessage());
		}
		catch (GVFCommunicationException e) {
			logger.error(e.getMessage());
		}
		return data;
	}

	
	/**
	 * Read a measurement from specified channel and convert it into format
	 * more suitable for converting into JSON.
	 * @param corbeltChanel channel from which to retrieve measurement
	 * @param dataAmount amount of measurement samples to retrieve
	 * @return List of MeasurementAtomicUnits
	 */
	private List<MeasurementAtomicUnit> generateData(List<CorBELTECGData> data) {
		List<MeasurementAtomicUnit> measurementValues = new ArrayList<MeasurementAtomicUnit>();
		
		for (CorBELTECGData d : data) {
			measurementValues.add(new MeasurementAtomicUnit(d.getDate(), new Double((d.getECGData()*0.122))));
		}
		return measurementValues;	
	}	
}
